package xin.alum.aim.handler;

import com.google.protobuf.InvalidProtocolBufferException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroupFuture;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import xin.alum.aim.constant.AimAttr;
import xin.alum.aim.model.Aim;
import xin.alum.aim.constant.AimClose;
import xin.alum.aim.constant.AimPlatform;
import xin.alum.aim.groups.Session;
import xin.alum.aim.groups.SessionGroups;
import xin.alum.aim.groups.Sessions;
import xin.alum.aim.model.Events;
import xin.alum.aim.model.ProtoUtil;
import xin.alum.aim.util.GZipUtil;

import java.io.InputStream;

/**
 * 此类为源码测试接受者
 * 正式业务请重写此服务类
 *
 * @author alum(alum @ live.cn)
 * @date 2021/8/5 10:01
 */
public class AimReceiver implements AimReceive {

    /**
     * 日志对象
     */
    protected final InternalLogger logger = InternalLoggerFactory.getInstance(this.getClass());
    /**
     * 所有会话
     */
    @Autowired
    protected Sessions sessions;
    /**
     * 所有群组
     */
    @Autowired
    protected SessionGroups groups;

    /**
     * WebSocket握手处理逻辑，绑定用户信息到会话系统
     *
     * @param ch  当前连接通道
     * @param req 当前上行请求,URL参数已经放入ch中
     * @param res 握手失败 return false 输出给客户端的对象
     * @return
     */
    @Override
    public boolean onHandShake(Channel ch, FullHttpRequest req, FullHttpResponse res) {
        return true;
    }

    /**
     * Socks基于自定义Bing握手处理逻辑，绑定用户信息到会话系统
     *
     * @param ch 通道
     * @param in 握手数据包
     * @return
     */
    @Override
    public boolean onHandShake(Channel ch, ByteBuf in) {
        return true;
    }

    /**
     * Socks基于ProtoBuf-Login的握手处理逻辑
     *
     * @param ch
     * @param token
     * @return
     */
    @Override
    public boolean onHandShake(Channel ch, String token) {
        return true;
    }

    @Override
    public void onHandShaked(Channel ch) {
        if (sessions != null) {
            sessions.bindUser(ch, ch.id().toString(), AimPlatform.NON, "");
        }
    }

    @Override
    public void onPing(Channel ch) {
        logger.debug("{} 用户【{}】通过{}发来PING", ch, ch.attr(AimAttr.UID), ch.attr(AimAttr.PLATFORM));
    }

    /**
     * Text类型数据处理，测试用，正式业务请重写
     *
     * @param ch
     * @param text
     */
    @Override
    public void onText(Channel ch, String text) {
        text = GZipUtil.ungzip(xin.alum.aim.Aim.properties.getGzip(), text);
        ProtoUtil protoUtil = new ProtoUtil(Aim.Data.getDescriptor());
        Aim.Packet p = (Aim.Packet) protoUtil.toProto(Aim.Packet.newBuilder(), text);
        try {
            Aim.Data d = p.getData().unpack(Aim.Data.class);
            onRecive(ch, new Events<>(p.getKey(), d));
        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }


    /**
     * 处理WebSocket binary请求
     * 测试方法，正式业务请重写
     *
     * @param ch
     * @param in
     * @throws InvalidProtocolBufferException
     */
    @Override
    public void onByte(Channel ch, ByteBuf in) {
        try {
            if (ch.attr(AimAttr.AGREEMENT).get() == Aim.Agreement.Binary) {
                logger.error("{} {}协议未实现", ch, Aim.Agreement.Binary);
                return;
            }
            InputStream inputStream = new ByteBufInputStream(in);
            Aim.Packet m = Aim.Packet.parseFrom(inputStream);
            Events t = new Events<>(m.getKey(), m.getTimestamp(), m.getData());
            onRecive(ch, t);
        } catch (Exception e) {
            logger.error("{} 数据解析异常,{},{}", ch.id(), e.getMessage(), e);
        }
    }

    /**
     * 测试方法，正式业务请重写
     *
     * @param ch
     * @param s
     */
    @Override
    public void onRecive(Channel ch, Events s) {
        Events events = new Events(s.getKey());
        events.setData(s.getData());
        ChannelGroupFuture f = sessions.sends(s);
//        final int size = sessions.size();
//        f.addListener(t -> {
//            long time = System.currentTimeMillis() - events.getTimestamp();
//            if (time > 1000 || f.isPartialFailure()) {
//                logger.warn("{} 向【{}】群发【{}】人{},用时-{}", ch, sessions.name(), size, t.isSuccess(), time);
//            }
//        });
    }

    @Override
    public Events onKick(Channel old, Session session) {
        logger.warn("{} 用户【{}】通过{}-{}登录", old, session.getUid(), session.getPlatform(), session.getClientIp());
        return new Events("kick", String.format("您已通过[%s-%s]登录", session.getPlatform(), session.getClientIp()));
    }

    @Override
    public void onClose(Channel ch, AimClose close) {
        logger.warn("{} UID-{}在PF-{},因{}而关闭,连接数{}.", ch, ch.attr(AimAttr.UID), ch.attr(AimAttr.PLATFORM), close.name(), sessions != null ? sessions.size() : 0);
    }

}
